/// \file
/// \ingroup tutorial_graphics
/// \notebook -js
/// Using TExec to handle keyboard events and TComplex to draw the Mandelbrot set.
///
/// Pressing the keys 'z' and 'u' will zoom and unzoom the picture
/// near the mouse location, 'r' will reset to the default view.
///
/// Try it (in compiled mode!) with:   `root mandelbrot.C+`
///
/// \macro_image (tcanvas_js)
///
/// ### Details
///
///    when a mouse event occurs the myexec() function is called (by
///    using AddExec). Depending on the pressed key, the mygenerate()
///    function is called, with the proper arguments. Note the
///    last_x and last_y variables that are used in myexec() to store
///    the last pointer coordinates (px is not a pointer position in
///    kKeyPress events).
///
/// \macro_code
///
/// \author Luigi Bardelli <bardelli@fi.infn.it>

#include <TStyle.h>
#include <TROOT.h>
#include <TH2.h>
#include <TComplex.h>
#include <TVirtualPad.h>
#include <TCanvas.h>

TH2F *last_histo=NULL;

void mygenerate(double factor, double cen_x, double cen_y)
{
  printf("Regenerating...\n");
  // resize histo:
  if(factor>0)
    {
      double dx=last_histo->GetXaxis()->GetXmax()-last_histo->GetXaxis()->GetXmin();
      double dy=last_histo->GetYaxis()->GetXmax()-last_histo->GetYaxis()->GetXmin();
      last_histo->SetBins(
                          last_histo->GetNbinsX(),
                          cen_x-factor*dx/2,
                          cen_x+factor*dx/2,
                          last_histo->GetNbinsY(),
                          cen_y-factor*dy/2,
                          cen_y+factor*dy/2
                          );
      last_histo->Reset();
    }
  else
    {
      if(last_histo!=NULL) delete last_histo;
      // allocate first view...
      last_histo= new TH2F("h2",
         "Mandelbrot [move mouse and  press z to zoom, u to unzoom, r to reset]",
                           200,-2,2,200,-2,2);
      last_histo->SetStats(0);
    }
  const int max_iter=50;
  for(int bx=1;bx<=last_histo->GetNbinsX();bx++)
    for(int by=1;by<=last_histo->GetNbinsY();by++)
      {
         double x=last_histo->GetXaxis()->GetBinCenter(bx);
         double y=last_histo->GetYaxis()->GetBinCenter(by);
         TComplex point( x,y);
         TComplex z=point;
         int iter=0;
         while (z.Rho()<2){
            z=z*z+point;
            last_histo->Fill(x,y);
            iter++;
            if(iter>max_iter) break;
         }
      }
  last_histo->SetContour(99);
  last_histo->Draw("colz");
  gPad->Modified();
  gPad->Update();
  printf("Done.\n");
}

void myexec()
{
  // get event information
  int event = gPad->GetEvent();
  int px = gPad->GetEventX();
  int py = gPad->GetEventY();

  // some magic to get the coordinates...
  double xd = gPad->AbsPixeltoX(px);
  double yd = gPad->AbsPixeltoY(py);
  float x = gPad->PadtoX(xd);
  float y = gPad->PadtoY(yd);

  static float last_x;
  static float last_y;

  if(event!=kKeyPress)
    {
      last_x=x;
      last_y=y;
      return;
    }

  const double Z=2.;
  switch(px){
  case 'z': // ZOOM
    mygenerate(1./Z, last_x, last_y);
    break;
  case 'u': // UNZOOM
    mygenerate(Z   , last_x, last_y);
    break;
  case 'r': // RESET
    mygenerate(-1   , last_x, last_y);
    break;
  };
}

void mandelbrot()
{
  // cosmetics...
  gStyle->SetPadGridX(kTRUE);
  gStyle->SetPadGridY(kTRUE);
  new TCanvas("canvas","View Mandelbrot set");
  // this generates and draws the first view...
  mygenerate(-1,0,0);

  // add exec
  gPad->AddExec("myexec","myexec()");
}
